package edu.purdue.touch.piccolo.event;

import java.awt.Shape;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.Hashtable;


import TUIO.TuioClient;
import TUIO.TuioCursor;
import TUIO.TuioObject;
import TUIO.TuioTime;

import edu.purdue.touch.piccolo.bridge.PTouchInterface;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.util.PAffineTransform;

public class PTouchEventManager {
	
	private static ArrayList<PNode> object = new ArrayList<PNode>(); // variable used to get the frame to which this listener is going to listen
	private static ArrayList<Integer> add = new ArrayList<Integer>();
	private static ArrayList<Integer> update = new ArrayList<Integer>();
	
	
	// Create the listener list
    private static javax.swing.event.EventListenerList listenerList =
        new javax.swing.event.EventListenerList();
    
    private static Hashtable<PTouchListener, PNode> listernerTable = new Hashtable<PTouchListener, PNode>();
    
    private static PTouchEventManager instance;
    
    public int screenX = Toolkit.getDefaultToolkit().getScreenSize().width;
    public int screenY = Toolkit.getDefaultToolkit().getScreenSize().height;
        
//    private TouchLogger touchLogger;

    /**
     * Constructor (private since this is a singleton class).
     */
   	private PTouchEventManager() {
   		TuioClient client = new TuioClient();
		PTouchInterface touchInterface = new PTouchInterface();
        client.addTuioListener(touchInterface);
    	client.connect();
    	
    	if (client.isConnected()) {
    		System.err.println("Connected!");
    	}
    	
//    	touchLogger = new TouchLogger();
	}	
   	
   	public static PTouchEventManager getInstance() { 
   		if (instance == null) { 
   			instance = new PTouchEventManager();
   		}
   		return instance;
   	}

   	
   	/**
   	 * Allows classes to register for events.
   	 * @param comp
   	 * @param listener
   	 */
    public void addTouchEventListener(PNode comp, PTouchListener listener) {
        listenerList.add(PTouchListener.class, listener);
        object.add(comp);
        listernerTable.put(listener,comp);
    }

    /**
     * Allows classes to unregister for Events.
     * @param comp
     * @param listener
     */
    public void removeMyEventListener(PNode comp, PTouchListener listener) {
        listenerList.remove(PTouchListener.class, listener);
        object.remove(comp);
        listernerTable.remove(listener);
    }
    
    /** 
     * Remove all touch listeners.
     */
    public void removeAllListeners() {
    	Object[] listeners = listenerList.getListenerList();
        for (int i = 0; i < listeners.length; i+=2) {
            if (listeners[i]==PTouchListener.class) {
            	listenerList.remove(PTouchListener.class,(PTouchListener)listeners[i+1] );
            }
        }
    	object.clear();
    	listernerTable.clear();
    }

    /**
     * Fire refresh event.
     * @param ttime
     */
    public void fireRefreshEvent(TuioTime ttime) {
        Object[] listeners = listenerList.getListenerList();
        // Each listener occupies two elements - the first is the listener class
        // and the second is the listener instance
        for (int i=0; i<listeners.length; i+=2) {
            if (listeners[i]==PTouchListener.class) {
             		((PTouchListener)listeners[i+1]).TouchEventOccurred(ttime);
            		
            }
        }
    }
    
    /**
     * Fire add event.
     * @param ttime
     */
    public void fireaddEvent(TuioCursor tcur) {

    	//Add to Logger
    	//touchLogger.addTouch(tcur);

    	Object[] listeners = listenerList.getListenerList();
    	add.clear();
    	// Each listener occupies two elements - the first is the listener class
    	// and the second is the listener instance

    	for (int i=0; i<listeners.length; i+=2) {
    		if (listeners[i]==PTouchListener.class) {
    			//if (contains( tcur, ((PNode)object.get(i/2)), object_bound.get(i/2))){
    			if (contains( tcur, ((PNode)object.get(i/2)))){
    				//contained_index_list.add(i);
    				//((PNode)object.get(i/2)).moveToFront();
    				add.add(i/2);

    			}
    		}
    	}

    	try {
    		//TODO: NULLPOINT Exception
    		boolean tt = true;
    		int front = 0; // variable to keep track `of the front most element on the screen
    		int top = 0;
    		Object []pp;

    		if (object.get(0).getRoot() == null) {
    			pp = (Object[])(object.get(0)).getAllNodes().toArray();
    		}
    		else {
    			pp = (Object[])(object.get(0)).getRoot().getAllNodes().toArray();
    		}

    		for (int i=0; i<add.size(); i++) {
    			if((object.get(add.get(i)).getVisible() && object.get(add.get(i)).getPickable())) {
    				if (((PNode)object.get(add.get(i))).getRoot() != null ){
    					if (tt) {
    						for (int jj =0; jj< pp.length; jj++){
    							if (pp[jj].equals(object.get(add.get(i)))){     			
    								front = add.get(i);
    								top = jj;

    								tt = false;
    							}
    						}
    					}
    					else {
    						for (int jj =0; jj< pp.length; jj++){
    							if (pp[jj].equals(object.get(add.get(i))) && jj > top ){     			
    								front = add.get(i);
    								top = jj;

    							}
    						}   
    					}
    				}
    			}
    		}

    		//System.out.println("tapped" + ((PNode)object.get(add.get(min))).getBounds());
    		//if (min >= 0 && min<listeners.length)
    		if (contains( tcur, ((PNode)object.get(front)))){
    			((PTouchListener) listeners[front*2+1]).TouchaddEventOccurred(tcur);
    		}
    	}
    	catch (NullPointerException e) {
    		e.printStackTrace();
    	}
    }
    public void fireremoveEvent(TuioCursor tcur) {
    	//Add to Logger
    	//touchLogger.addTouch(tcur);
    	
        Object[] listeners = listenerList.getListenerList();
        // Each listener occupies two elements - the first is the listener class
        // and the second is the listener instance
        for (int i=0; i<listeners.length; i+=2) {
            if (listeners[i]==PTouchListener.class) {
                ((PTouchListener)listeners[i+1]).TouchremoveEventOccurred(tcur);
            }
        }
    }

    public void fireupdateEvent(TuioCursor tcur) {
    	//Add to Logger
    	//touchLogger.addTouch(tcur);
    	
        Object[] listeners = listenerList.getListenerList();
        // Each listener occupies two elements - the first is the listener class
        // and the second is the listener instance
        
        update.clear();
        // Each listener occupies two elements - the first is the listener class
        // and the second is the listener instance
        
        for (int i=0; i<listeners.length; i+=2) {
            if (listeners[i]==PTouchListener.class) {
            	//if (contains( tcur, ((PNode)object.get(i/2)), object_bound.get(i/2))){
            	if (contains( tcur, ((PNode)object.get(i/2)))){
            		//contained_index_list.update(i);
            		//((PNode)object.get(i/2)).moveToFront();
            		update.add(i/2);
            		
            	}
            }
        }
                
        boolean tt = true;
        int front = 0; // variable to keep track of the front most element on the screen
        int top = 0;
        
        Object []pp;
        
        if (object.get(0).getRoot() == null) {
        	pp = (Object[])(object.get(0)).getAllNodes().toArray();
        }
    	else {
    		pp = (Object[])(object.get(0)).getRoot().getAllNodes().toArray();
    	}
        for (int i = 0; i < update.size(); i++) {
        	if (((PNode)object.get(update.get(i))).getRoot() != null ){
        		if (tt) {
        			for (int jj = 0; jj < pp.length; jj++){
        				if (pp[jj].equals(object.get(update.get(i)))){     			
        					front = update.get(i);
        					top = jj;
        					tt = false;
        				}
        			}
        		}
        		else {
        			for (int jj = 0; jj < pp.length; jj++){
        				if (pp[jj].equals(object.get(update.get(i))) && jj > top ){     			
        					front = update.get(i);
        					top = jj;
        				}
        			}   
        		}
    		}
    	}
        
        
        //System.out.println("tapped" + ((PNode)object.get(update.get(min))).getBounds());
        //if (min >= 0 && min<listeners.length)
        if (contains( tcur, ((PNode)object.get(front)))){
        	((PTouchListener)listeners[front*2+1]).TouchupdateEventOccurred(tcur);
        }
        
      /*
        
        for (int i=0; i<listeners.length; i+=2) {
            if (listeners[i]==PTouchListener.class) {
            	//if (contains( tcur, ((PNode)object.get(i/2)), object_bound.get(i/2))){
            	if (contains( tcur, ((PNode)object.get(i/2)))){
            		((PTouchListener)listeners[i+1]).TouchupdateEventOccurred(tcur);
            	}
            }
        }
        */
    }
    
 /*   
    public boolean contains(TuioCursor tcur, PNode node, PBounds bb)
	{
		AffineTransform at = node.getTransform();
		
		//Shape s = at.createTransformedShape(bb.getBounds2D());
		//Shape s = at.createTransformedShape(new Rectangle2D.Double(0,0,node.getWidth(),node.getHeight()));
		Shape s = at.createTransformedShape(node.getBounds());
		System.out.println(node.getBounds());
		System.out.println(bb);
		System.out.println(s.getBounds());
		
		return s.contains(tcur.getScreenX(PTouchEventManager.getInstance().screenX),tcur.getScreenY(PTouchEventManager.getInstance().screenY));
	}
 */   
    public boolean contains(TuioCursor tcur, PNode node) {
    	PAffineTransform at = node.getLocalToGlobalTransform(null);
    	
		
		//Shape s = at.createTransformedShape(bb.getBounds2D());
		//Shape s = at.createTransformedShape(new Rectangle2D.Double(node.getX(),node.getY(),node.getWidth(),node.getHeight()));
		Shape s = at.createTransformedShape(node.getBounds());
		
		return s.contains(tcur.getScreenX(PTouchEventManager.getInstance().screenX),tcur.getScreenY(PTouchEventManager.getInstance().screenY));
	}
    
    public PNode getNode(PTouchListener l) {
    	return listernerTable.get(l);
    }

	public void objectAdded(TuioObject tobj) {	
        Object[] listeners = listenerList.getListenerList();
        
        for (int i = 0; i < listeners.length; i += 2) {
            if (listeners[i]==PTouchListener.class) {
            	((PTouchListener)listeners[i+1]).ObjectaddEventOccurred(tobj);
            }
        }
	}

	public void objectRemoved(TuioObject tobj) {
		Object[] listeners = listenerList.getListenerList();
        
        for (int i = 0; i < listeners.length; i += 2) {
            if (listeners[i]==PTouchListener.class)
            {
            	((PTouchListener)listeners[i+1]).ObjectremoveEventOccurred(tobj);
            }
        }
	}

	public void objectUpdated(TuioObject tobj) {
		Object[] listeners = listenerList.getListenerList();
        
        for (int i=0; i<listeners.length; i+=2) {
            if (listeners[i]==PTouchListener.class) {
            	((PTouchListener)listeners[i+1]).ObjectupdateEventOccurred(tobj);
            }
        }
	}
	
}
